package com.ruoyi.service;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.flowable.FlowComment;
import com.ruoyi.common.flowable.FlowServiceFactory;
import com.ruoyi.common.flowable.ProcessConstants;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.dto.FlowProcDefDTO;
import com.ruoyi.dto.FlowTaskDTO;
import com.ruoyi.entity.ActReDeployment;
import com.ruoyi.entity.SysForm;
import com.ruoyi.mapper.SysDeployFormMapper;
import com.ruoyi.vo.FlowSaveXmlVO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.DeploymentBuilder;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceBuilder;
import org.flowable.image.impl.DefaultProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 流程定义
 *
 * @author XuanXuan
 *
 */
@Service
@Slf4j
public class FlowDefinitionServiceImpl extends FlowServiceFactory {

    @Resource
    private SysDeployFormServiceImpl sysDeployFormService;

    @Autowired
    private SysDeployFormMapper sysDeployFormMapper;

    private static final String BPMN_FILE_SUFFIX = ".bpmn";


    public boolean exist(String processDefinitionKey) {
        ProcessDefinitionQuery processDefinitionQuery
                = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey);
        long count = processDefinitionQuery.count();
        return count > 0 ? true : false;
    }


    /**
     * 流程定义列表
     *
     * @param pageNum  当前页码
     * @param pageSize 每页条数
     * @return 流程定义分页列表数据
     */

    public Page<FlowProcDefDTO> list(String name, Integer pageNum, Integer pageSize) {
        Page<FlowProcDefDTO> page = new Page<>();
        PageHelper.startPage(pageNum, pageSize);
        final List<FlowProcDefDTO> dataList = sysDeployFormMapper.selectDeployList(name);
        page.setTotal(new PageInfo(dataList).getTotal());
        page.setRecords(dataList);
        return page;
    }

    public List<FlowProcDefDTO> getDataFormFlowList(String name) {
        final List<FlowProcDefDTO> dataList = sysDeployFormMapper.getDataFormFlowList(name);
        // 加载挂表单
        for (FlowProcDefDTO procDef : dataList) {
            SysForm sysForm = sysDeployFormService.selectSysDeployFormByDeployId(procDef.getDeploymentId());
            if (Objects.nonNull(sysForm)) {
                procDef.setFormName(sysForm.getFormName());
                procDef.setFormId(sysForm.getFormId());
            }
        }
        return dataList;
    }


    /**
     * 导入流程文件
     *
     * @param in
     */

    public Deployment importFile(FlowSaveXmlVO vo, InputStream in) {
        ActReDeployment old = actReDeploymentMapper.getByKey(vo.getKey());
        Deployment deploy = repositoryService.createDeployment().addInputStream(vo.getName() + BPMN_FILE_SUFFIX, in)
                .key(vo.getKey()).name(vo.getName()).deploy();

        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult();
        ActReDeployment actReDeployment = actReDeploymentMapper.selectById(definition.getDeploymentId());
        actReDeployment.setInUse(true);
        actReDeployment.setType(old != null && old.getType() != null ? old.getType() : "ProjectApply");
        actReDeployment.updateById();

        return deploy;

    }

    /**
     * 创建流程
     */

    public ProcessDefinition createDeployment(ActReDeployment vo) {
        DeploymentBuilder builder =  repositoryService.createDeployment();
        builder.key(RandomUtil.randomString(10));
        builder.name(vo.getName());
        Deployment deploy = builder.deploy();
        ActReDeployment actReDeployment =  actReDeploymentMapper.selectById(deploy.getId());
        actReDeployment.setType(vo.getType());
        actReDeployment.setInUse(true);
        actReDeployment.updateById();

        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult();
        return definition;

    }

    public Object getByDeploymentId(String deploymentId) throws IOException {
        ActReDeployment actReDeployment = actReDeploymentMapper.getById(deploymentId);
        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
        FlowSaveXmlVO vo = new FlowSaveXmlVO();
        vo.setId(deploymentId);
        vo.setName(actReDeployment.getName());
        vo.setKey(actReDeployment.getKey());
        if (definition != null) {
            InputStream inputStream = repositoryService.getResourceAsStream(definition.getDeploymentId(), definition.getResourceName());
            //String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8.name());

            // Java流转String
            String resultXml = new BufferedReader(new InputStreamReader(inputStream, "utf-8"))
                    .lines().collect(Collectors.joining(System.lineSeparator()));

            vo.setXml(resultXml);
            vo.setProcessId(definition.getKey());
            vo.setProcessName(definition.getName());
        }

        return vo;
    }

    public AjaxResult readXmlByKey(String key) throws IOException {
        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(key).latestVersion().singleResult();
        InputStream inputStream = repositoryService.getResourceAsStream(definition.getDeploymentId(), definition.getResourceName());
        String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8.name());
        return AjaxResult.success("", result);
    }

    /**
     * 读取xml
     *
     * @param deployId
     * @return
     */

    public InputStream readImage(String deployId) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult();
        //获得图片流
        DefaultProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGenerator();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
        //输出为图片
        return diagramGenerator.generateDiagram(
                bpmnModel,
                "png",
                Collections.emptyList(),
                Collections.emptyList(),
                "宋体",
                "宋体",
                "宋体",
                null,
                1.0,
                false);

    }

    /**
     * 根据流程定义ID启动流程实例
     *
     * @param procDefId 流程定义Id
     * @param variables 流程变量
     * @return
     */
    public AjaxResult startProcessInstanceById(String procDefId, Map<String, Object> variables) {
        try {
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefId)
                    .latestVersion().singleResult();
            startProcessInstanceById(processDefinition, variables);
            return AjaxResult.success("流程启动成功");
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.error("流程启动错误");
        }
    }

    public ProcessInstance startProcessInstanceById(ProcessDefinition processDefinition, Map<String, Object> variables) {
        if (Objects.nonNull(processDefinition) && processDefinition.isSuspended()) {
            throw new ServiceException("流程已被挂起,请先激活流程");
        }
        Iterator it = variables.keySet().iterator();
        while (it.hasNext()) {
            String key = (String) it.next();
            Object val = variables.get(key);
            if (key.startsWith("l_")) {
                variables.put(key, val == null ? (Long) val : Long.valueOf(val.toString()));
            } else if (key.startsWith("i_")) {
                variables.put(key, val == null ? (Integer) val : Integer.valueOf(val.toString()));
            } else if (key.startsWith("s_")) {
                variables.put(key, val == null ? (String) val : val.toString());
            } else if (key.startsWith("b_")) {
                variables.put(key, val == null ? (Boolean) val : Boolean.valueOf(val.toString()));
            }
        }
//           variables.put("skip", true);
//           variables.put(ProcessConstants.FLOWABLE_SKIP_EXPRESSION_ENABLED, true);
        // 设置流程发起人Id到流程中

        SysForm sysForm = sysFormMapper.getByDeployId(processDefinition.getDeploymentId());
        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
        identityService.setAuthenticatedUserId(sysUser.getUserId().toString());
        variables.put(ProcessConstants.PROCESS_INITIATOR, sysUser.getUserId());
        variables.put("startUserId", sysUser.getUserId());

        // 获取流程构造器
        ProcessInstanceBuilder processInstanceBuilder = runtimeService.createProcessInstanceBuilder();

        variables.put("formName", sysForm.getFormName());
        String titleFormat = sysForm.getTitleFormat();
        Iterator iterator = variables.keySet().iterator();
        while (iterator.hasNext()) {
            String key = (String) iterator.next();
            Object val = variables.get(key);
            if (val != null && val != null) {
                titleFormat = titleFormat.replaceAll("\\$" + key, val.toString());
            }
        }
        // 设置参数
        processInstanceBuilder.name(titleFormat); // 流程实例标题
        //processInstanceBuilder.businessKey("appxx");   // 业务key
        processInstanceBuilder.processDefinitionId(processDefinition.getId());  //流程定义key
        processInstanceBuilder.variables(variables);
        ProcessInstance processInstance = processInstanceBuilder.start();

        //ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId(), variables);

        // 给第一步申请人节点设置任务执行人和意见 todo:第一个节点不设置为申请人节点有点问题？
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).list();
        if (Objects.nonNull(tasks)) {
            for (int i = 0; i < tasks.size(); i++) {
                Task task = tasks.get(i);
                setAssignee(task);
                if (i == 0) {
                    if (Objects.nonNull(task)) {
                        taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), FlowComment.NORMAL.getCode(),
                                sysUser.getNickName() + "申请" + sysForm.getFormName() + "【发起】");
                        taskService.complete(task.getId(), variables);
                    }
                }
            }
        }
        return processInstance;

    }

    private void setAssignee(Task task) {
        if (task != null) {
            return;
        }
        List<Process> processes = repositoryService.getBpmnModel(task.getProcessDefinitionId()).getProcesses();
        if (CollectionUtils.isEmpty(processes)) {
            return;
        }
        for (Process process : processes) {
            Collection<FlowElement> flowElements = process.getFlowElements();
            if (CollectionUtils.isEmpty(flowElements)) {
                continue;
            }
            for (FlowElement flowElement : flowElements) {
                if (!(flowElement instanceof UserTask)) {
                    continue;
                }
                UserTask userTask = (UserTask) flowElement;
                if (userTask.getId().equals(task.getTaskDefinitionKey())) {
                    System.out.println("1111");
                    String assignee = userTask.getAssignee();
                    taskService.addCandidateUser("", "");
                }
            }
        }

    }

    /**
     * 激活或挂起流程定义
     *
     * @param state    状态
     * @param deployId 流程部署ID
     */

    public void updateState(Integer state, String deployId) {
        ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult();
        // 激活
        if (state == 1) {
            repositoryService.activateProcessDefinitionById(procDef.getId(), true, null);
        }
        // 挂起
        if (state == 2) {
            repositoryService.suspendProcessDefinitionById(procDef.getId(), true, null);
        }
    }


    /**
     * 删除流程定义
     *
     * @param deployId 流程部署ID act_ge_bytearray 表中 deployment_id值
     */

    public void delete(String deployId) {
        // true 允许级联删除 ,不设置会导致数据库外键关联异常
        sysFormMapper.deleteByDeployId(deployId);
        repositoryService.deleteDeployment(deployId, true);
    }


    public void delDeploymentForm(String formId) {
        // true 允许级联删除 ,不设置会导致数据库外键关联异常
        SysForm sysForm = sysFormMapper.selectById(formId);
        sysFormMapper.deleteById(formId);
        try {
            repositoryService.deleteDeployment(sysForm.getDeployId(), true);
        } catch (Exception e) {

        }
    }


    private void addRow(List<Map<String, Object>> rows, String key, String name, String type) {
        Map row = new HashMap();
        row.put("key", key);
        row.put("name", name);
        row.put("type", type);
        rows.add(row);
    }

    public List<Map<String, Object>> getCanAssignTaskList(String procInsId) {
        FlowTaskDTO flowTaskDto = actHiProcinstMapper.getByProcInsId(procInsId);
        List rs = new ArrayList<>();
        if ("Document_issuing_process_receipt".equals(flowTaskDto.getProcDefKey())) {
            addRow(rs, "isPassRoundForPerusal", "是否传阅", "multiple");
            addRow(rs, "isUndertake", "是否承办", "single");
            addRow(rs, "isFileSave", "是否归档", "single");
        }
        return rs;
    }

    public void updateUseDeployment(ActReDeployment vo) {
        ActReDeployment actReDeployment = actReDeploymentMapper.getById(vo.getId());
        vo.setInUse(!actReDeployment.getInUse());
        vo.updateById();
    }

    public void deleteDeployment(ActReDeployment vo) {
        ActReDeployment actReDeployment = actReDeploymentMapper.getById(vo.getId());
        actReDeploymentMapper.updateDel(actReDeployment.getKey());
    }


    public AjaxResult readXml(String deployId) throws IOException {
        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult();
        InputStream inputStream = repositoryService.getResourceAsStream(definition.getDeploymentId(), definition.getResourceName());
        String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8.name());
        return AjaxResult.success("", result);
    }

}
